using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Common;

namespace ImageProcessing.Storage
{
    public interface IImageArchiver
    {
        LoadedImage LoadRawImage(string filename);
        void SaveRawImage(TagInfo tagInfo, int width, int height, ref byte[] capturedImage);
        List<TagInfo> ImageList { get; }
    }

    public abstract class BaseImageArchiver : IImageArchiver
    {
        public const int Version = 1;

        private volatile List<TagInfo> _imageList;
        public List<TagInfo> ImageList
        {
            get
            {
                if (_imageList == null)
                {
                    lock (this)
                    {
                        if (_imageList == null)
                        {
                            _imageList = new List<TagInfo>();

                            List<string> fileList = GetRawFilesListing();
                            foreach (string fileName in fileList)
                            {
                                _imageList.Add(GetRawFileTagInfo(fileName));
                            }
                        }
                    }
                }

                return _imageList;
            }
        }

        public LoadedImage LoadRawImage(string filename)
        {
            byte[] pixelData;
            int height, width;
            TagInfo tagInfo = new TagInfo();
            var fileStream = OpenStorageFileStream(filename);
            try
            {
                int version = BitConverter.ToInt32(ReadField(fileStream), 0);

                if (version != 1)
                    throw new NotImplementedException("only supports version 1");

                width = BitConverter.ToInt32(ReadField(fileStream), 0);
                height = BitConverter.ToInt32(ReadField(fileStream), 0);
                tagInfo.TagId = new Guid(ReadField(fileStream));
                tagInfo.TagName = ReadString(fileStream);

                var size = width * height;

                pixelData = new byte[size];
                fileStream.Read(pixelData, 0, size);
            }
            finally
            {
                fileStream.Close();
            }

            return new LoadedImage() { rawImage = pixelData, tagInfo = tagInfo, width = width, height = height };

        }

        protected static byte[] ReadField(Stream fileStream)
        {
            var fieldLength = fileStream.ReadByte();
            var fieldArray = new byte[fieldLength];
            fileStream.Read(fieldArray, 0, fieldLength);
            return fieldArray;
        }

        protected string ReadString(Stream fileStream)
        {
            var readString = ReadField(fileStream);
            return Encoding.UTF8.GetString(readString, 0, readString.Length);
        }



        public void SaveRawImage(TagInfo tagInfo, int width, int height, ref byte[] capturedImage)
        {
            var fileName = tagInfo.TagId + ".raw";

            using (var fileStream = CreateStorageFileStream(fileName))
            {
                WriteField(fileStream, BitConverter.GetBytes(Version));
                WriteField(fileStream, BitConverter.GetBytes(width));
                WriteField(fileStream, BitConverter.GetBytes(height));
                WriteField(fileStream, tagInfo.TagId.ToByteArray());
                WriteField(fileStream, Encoding.UTF8.GetBytes(tagInfo.TagName));
                WriteField(fileStream, capturedImage);
                fileStream.Flush();
                fileStream.Close();
            }

            ImageList.Add(tagInfo);
        }

        private void WriteField(Stream fileStream, byte[] byteArray)
        {
            fileStream.WriteByte((byte)byteArray.Length);
            fileStream.Write(byteArray, 0, byteArray.Length);
        }

        public TagInfo GetRawFileTagInfo(string filename)
        {
            LoadedImage rawData = LoadRawImage(filename);
            return rawData.tagInfo;
        }


        protected abstract Stream CreateStorageFileStream(string fileName);
        protected abstract Stream OpenStorageFileStream(string fileName);
        protected abstract List<string> GetRawFilesListing();
    }

    public class LoadedImage
    {
        public TagInfo tagInfo;
        public byte[] rawImage;
        public int width, height;
    }

}